/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/07/06
*/
#include "IWindow.h"
#include "../edui.h"

EDUI_NAMESPACE_BEGIN


IWindow::IWindow() : m_root(0), m_ctrlFocus(0), m_ctrlKeyboard(0), m_ctrlMouse(0), m_ctrlTab(0), m_cursor(0)
{}

IWindow::~IWindow()
{}

TextureMgr* IWindow::GetTextureMgr()
{
    return &m_textureMgr;
}

AnimationMgr* IWindow::GetAnimationMgr()
{
    return &m_animationMgr;
}

bool IWindow::SetRoot(Container* container)
{
    if (!container) return false;

    if (!container->SetWindow(this)){
        return false;
    }

    if (m_root){
        m_root->Release();
    }

    m_root = container;

    m_root->AutoLayout();

    this->Invaldate();

    return true;
}

Container* IWindow::GetRoot() const
{
    return m_root;
}

void IWindow::AskClose()
{
    Msg msg;
    msg.msg = MSG_WND_CLOSE;
    msg.wnd.wnd = this;
    msg.wnd.data1 = 0;
    IEnginePtr->PushMsg(&msg);
}

void IWindow::Close()
{
    Msg msg;
    msg.msg = MSG_WND_CLOSE;
    msg.wnd.wnd = this;
    msg.wnd.data1 = 1;
    IEnginePtr->PushMsg(&msg);
}

void IWindow::HandleMsg(Msg* msg)
{
    {
        bool bHandled = false;
        this->evt.handleMsg.emit(msg, &bHandled);
        if (bHandled) return;
    }
    
    if (msg->msg == MSG_WND_CLOSE){
        if (msg->wnd.data1 == 0){
            bool bClose = true;
            this->evt.askClose.emit(this, &bClose);
            if (bClose) {
                WindowMgr* mgr = IEnginePtr->GetWindowMgr();
                mgr->Remove(this);
                this->Destroy();
                this->Release();
            }
        } else if (msg->wnd.data1 == 1){
            WindowMgr* mgr = IEnginePtr->GetWindowMgr();
            mgr->Remove(this);
            this->Destroy();
            this->Release();
        }
    } else if (msg->msg == MSG_WND_EXPOSED){
        //window not handle msg MSG_WND_EXPOSED,in render.
        this->_Render(this->GetRender(), &m_invaldate);
        return;
    } else if (msg->msg == MSG_MOUSE_WHEEL){
        // wheel
        if (m_ctrlKeyboard && m_ctrlKeyboard->IsEnabled() && m_ctrlKeyboard->IsKeyboardEnabled()){
            m_ctrlKeyboard->HandleMsg(msg);
        }
        return;
    } else if (msg->msg == MSG_KEY_DOWN || msg->msg == MSG_KEY_UP || msg->msg == MSG_TEXT_EDITING || msg->msg == MSG_TEXT_INPUT){
        // keyboard msg
        if (m_ctrlKeyboard && m_ctrlKeyboard->IsEnabled() && m_ctrlKeyboard->IsKeyboardEnabled()){
            m_ctrlKeyboard->HandleMsg(msg);
        }
        // respond tab key
        if (msg->msg == MSG_KEY_DOWN){
            KeyboardMsg* evt = &msg->key;
            if (evt->key == KEY_TAB){
                this->TabCtrlNext();
            }
        }
        return;
    } else {
        if (msg->msg == MSG_MOUSE_BUTTON_DOWN){
            // set focus control
            Control* ctrl = this->FindControlByPt(msg->button.x, msg->button.y, FIND_NOVISIBLE || FIND_NODISABLED);
            this->SetFocusCtrl(ctrl);

            this->SetTabCtrl(0);
        } else if (msg->msg == MSG_MOUSE_MOVE){
            // set mouse control
            Control* ctrl = this->FindControlByPt(msg->motion.x, msg->motion.y, FIND_NOVISIBLE || FIND_NODISABLED);

            if (m_ctrlMouse != ctrl){
                if (m_ctrlMouse) m_ctrlMouse->OnMouseOut();
                m_ctrlMouse = ctrl;
                if (m_ctrlMouse) m_ctrlMouse->OnMouseIn();
            }
        } else if (msg->msg == MSG_WND_ENTER){
            IEnginePtr->SetCursor(m_cursor);
        } 

        if (m_root && m_root->IsVisible() && m_root->IsEnabled()){
            m_root->HandleMsg(msg);
        }
    }

    if ((msg->msg == MSG_WND_RESIZED) || (msg->msg == MSG_WND_SIZE_CHANGED)){
        this->_Render(this->GetRender(), 0);
    }
}

void IWindow::_Render(IRender* render, const Rect* rc)
{
    render->Clear();    
    render->SetBlendMode(BLENDMODE_BLEND);

    bool bHandled = false;
    this->evt.onRender.emit(render, rc, &bHandled);
    if (!bHandled) {
        if (!rc || !rc->IsOK()) {
            this->Render(render, nullptr);
        } else {
            this->Render(render, rc);
        }
    }
    
    render->Present(rc);

    m_invaldate.Clear();    
}

void IWindow::Render(IRender* render, const Rect* rc)
{
    if (m_root && m_root->IsVisible() && m_root->NeedRender(rc)){
        m_root->AutoLayout();
        m_root->Render(render, rc);

        if (m_ctrlTab){
            render->SetColor(255, 255, 255, 255);
            Rect rc = m_ctrlTab->GetRect();
            render->DrawRect(&rc);
        }
    }

    if (this->GetFlags() & WINDOW_BORDERLESS){
        render->SetColor(24, 180, 237, 255);
        Size sz = this->GetSize();
        render->DrawRect(0, 0, sz.w, sz.h);
    }
}

void IWindow::Invaldate(const Rect* rc)
{
    //this->_Render(this->GetRender(), rc);

    Msg msg;
    msg.msg = MSG_WND_EXPOSED;
    msg.wnd.wnd = this;
    msg.wnd.data1 = 0;
    
    m_invaldate.Intersect(rc);

    IEnginePtr->PushMsg(&msg);
}

Control* IWindow::FindControl(const char* name)
{
    if (!name || !m_root) return 0;
    return m_root->FindControl(name);
}

Control* IWindow::FindControlByTabID(int id)
{
    if (!m_root) return 0;
    return m_root->FindControlByTabID(id);
}

Control* IWindow::FindControlByPt(int x, int y, Uint32 flag)
{
    if (!m_root) return 0;
    return m_root->FindControlByPt(x, y, flag);
}

ITexture*  IWindow::FindTexture(const char* name, bool autoload)
{
    ITexture* texture = m_textureMgr.Get(name);
    if (autoload && !texture){
        IEnginePtr->LoadTexture(this, name);
        texture = m_textureMgr.Get(name);
    }
    return texture;
}

ITexture*  IWindow::FindTexture(int id, bool autoload)
{
    ITexture* texture = m_textureMgr.Get(id);
    if (autoload && !texture){
        IEnginePtr->LoadTexture(this, id);
        texture = m_textureMgr.Get(id);
    }
    return texture;
}

Animation* IWindow::FindAnimation(const char* name)
{
    return m_animationMgr.Get(name);
}

Animation* IWindow::FindAnimation(int id)
{
    return m_animationMgr.Get(id);
}

void IWindow::PushMsg(Msg *msg)
{
    msg->wnd.wnd = this;
    IEnginePtr->PushMsg(msg);
}

bool IWindow::LoadLayout(const char* xmlfile)
{
    return IEnginePtr->LoadLayout(this, xmlfile);
}

bool IWindow::LoadLayoutFromStr(const char* xmlstr)
{
    return IEnginePtr->LoadLayoutFromStr(this, xmlstr);
}

Control* IWindow::LoadControl(const char* xmlfile)
{
    return IEnginePtr->LoadControl(this, xmlfile);
}

Control* IWindow::LoadControlFromStr(const char* xmlstr)
{
    return IEnginePtr->LoadControlFromStr(this, xmlstr);
}

bool IWindow::LoadAnimation(const char* xmlstr)
{
    if (!xmlstr || !this->IsOK()) return false;
    TiXmlElement xml(xmlstr);
    return this->LoadAnimation(xml.FirstChildElement());
}

bool IWindow::LoadAnimation(TiXmlElement* root)
{
    if (!root || !this->IsOK()) return false;
    int id = -1;
    if (!root->Attribute("id", &id)) id = -1;
    const char* name = root->Attribute("name");
    if (!IEnginePtr->LoadAnimation(this, name, id)){
        Log("[EDUI] Window::LoadAnimation : load %s error.", name == 0 ? "" : name);
        return false;
    }

    for(TiXmlElement* child = root->FirstChildElement(); child; child = child->NextSiblingElement()){
        if (strcmp(child->Value(), "Frame") == 0){
            int id = -1, imgid = -1, delay = 0;
            if (!child->Attribute("id", &id)) id = -1;
            if (!child->Attribute("imgid", &imgid)) imgid = -1;
            if (!child->Attribute("delay", &delay)) delay = 0;
            const char* img = child->Attribute("img");

            if (!delay){
                bool bLoaded = false;
                if (imgid == -1) bLoaded = IEnginePtr->LoadAnimationFrame(this, name, img, id);
                else bLoaded = IEnginePtr->LoadAnimationFrame(this, name, imgid, id);
                if (!bLoaded){
                    Log("[EDUI] Window::LoadAnimation : load %s frame %s imgid:%d error.", name == 0 ? "" : name, img == 0 ? "" : img, imgid);
                }
            } else {
                AnimationFrame frame;
                if (imgid == -1) frame.texture = this->FindTexture(img, true);
                else frame.texture = this->FindTexture(imgid, true);
                if (frame.texture){
                    int num = frame.texture->GetNum();
                    for (int i = 0; i < num; ++i){
                        Point pt = frame.texture->GetOffset(i);
                        Size sz = frame.texture->GetSize(i);
                        frame.id = i;
                        frame.x = pt.x;
                        frame.y = pt.y;
                        frame.w = sz.w;
                        frame.h = sz.h;
                        frame.delay = delay;
                        if (!IEnginePtr->LoadAnimationFrame(this, name, &frame, id)){
                            Log("[EDUI] Window::LoadAnimation : load %s frame %s imgid:%d %d error.", name == 0 ? "" : name, img == 0 ? "" : img, imgid, i);
                        }
                    }
                } else {
                    Log("[EDUI] Window::LoadAnimation : load %s frame %s imgid:%d error.", name == 0 ? "" : name, img == 0 ? "" : img, imgid);
                }
            }
        }
    }

    return true;
}

void IWindow::SetAttribute(const char* name, const char* value)
{
    if (0 == strcmp(name, "name")){
        this->SetName(value);
    } if (0 == strcmp(name, "title")){
        //title
        this->SetTitle(value);
    } else if (0 == strcmp(name, "caption")){
        //caption
        Rect rc = ParserRect(value);
        this->SetCaptionRect(&rc);
    } else if (0 == strcmp(name, "sizebox")){
        //border sizebox
        Rect1 rc = ParserRect1(value);
        this->SetSizeBox(&rc);

        /*Uint32 flags = this->GetFlags();
        if ( (rc.l == 0) && (rc.t == 0) && (rc.r == 0) && (rc.b == 0)){
            flags |= WINDOW_BORDERLESS; 
        } else {
            flags &= ~WINDOW_BORDERLESS;
        }
        this->SetFlags(flags);*/
    } else if (0 == strcmp(name, "pos")){
        //window pos
        Point pt = ParserPoint(value);
        this->SetPos(pt.x, pt.y);
    } else if (0 == strcmp(name, "size")){
        //window size
        Size sz = ParserSize(value);
        this->SetSize(sz.w, sz.h);
    } else if (0 == strcmp(name, "maxsize")){
        //window max size
        Size sz = ParserSize(value);
        this->SetMaxSize(sz.w, sz.h);
    } else if (0 == strcmp(name, "minsize")){
        //window min size
        Size sz = ParserSize(value);
        this->SetMinSize(sz.w, sz.h);
    } else if (0 == strcmp(name, "font")){
        //default font:name or id
        FontMgr* fontMgr = IEnginePtr->GetFontMgr();
        IFont* font = fontMgr->Get(value);
        if (font) this->SetFont(font);
    } else if (0 == strcmp(name, "fontid")){
        int id = -1;
        if (ParserInt(value, &id, -1) && (id >= 0)){
            FontMgr* fontMgr = IEnginePtr->GetFontMgr();
            IFont* font = fontMgr->Get(id);
            if (font) this->SetFont(font);
        }
    } else if (0 == strcmp(name, "cursor")){
        CursorMgr* mgr = IEnginePtr->GetCursorMgr();
        ICursor* cursor = mgr->Get(value);
        this->SetCursor(cursor);
    } else if (0 == strcmp(name, "cursorid")){
        int i = 0;
        if (ParserInt(value, &i)){
            CursorMgr* mgr = IEnginePtr->GetCursorMgr();
            ICursor* cursor = mgr->Get(i);
            this->SetCursor(cursor);
        }
    }
}

void IWindow::AfterSetAttribute()
{}

void IWindow::SetCursor(ICursor* cursor)
{
    m_cursor = cursor;
}

ICursor* IWindow::GetCursor() const
{
    return m_cursor;
}

void IWindow::SetFocusCtrl(Control* ctrl)
{
    if (m_ctrlFocus == ctrl) return;
    
    if (m_ctrlFocus) m_ctrlFocus->OnKillFocus();
    m_ctrlFocus = ctrl;
    m_ctrlKeyboard = ctrl;
    if (m_ctrlFocus) m_ctrlFocus->OnFocus();
}

Control* IWindow::GetFocusCtrl() const
{
    return m_ctrlFocus;
}

void IWindow::SetTabCtrl(Control* ctrl)
{
    if (m_ctrlTab == ctrl) return;
    
    if (ctrl){
        if (m_ctrlTab) m_ctrlTab->Invaldate();
        m_ctrlTab = ctrl;
        if (m_ctrlTab) m_ctrlTab->Invaldate();
    } else {
        if (m_ctrlTab){
            Control* parent = m_ctrlTab->GetParent();
            if (!parent){
                m_ctrlTab = 0;
                this->Invaldate();
            } else {
                m_ctrlTab = 0;
                parent->Invaldate();
            }
        }
    }

}

Control* IWindow::GetTabCtrl() const
{
    return m_ctrlTab;
}

void IWindow::TabCtrlNext()
{
    int tab = 0;
    if (m_ctrlTab) tab = m_ctrlTab->GetTabID();
    ++tab;
    Control* ctrl = FindControlByTabID(tab);
    if (!ctrl && tab != 1) ctrl = FindControlByTabID(1);
    this->SetFocusCtrl(ctrl);
    this->SetTabCtrl(ctrl);
}

//0 error
TimerID IWindow::AddTimer(Uint32 interval, TimerCallback callback, void *data1, void* data2)
{
    return IEnginePtr->AddTimer(interval, callback, this, data1, data2);
}

void IWindow::RemoveTimer(TimerID id)
{
     IEnginePtr->RemoveTimer(id);
}

EDUI_NAMESPACE_END
